﻿using System;
using System.IO;
using System.Collections;
using System.IO.Compression;

namespace personalplaner.core.zip
{
	public class GZipDecompress
	{
		public static GZipResult Decompress(string sourceFolder, string destFolder, string zipFileName)
		{
			return Decompress(sourceFolder, destFolder, zipFileName, true);
		}

		public static GZipResult Decompress(string srcFolder, string destFolder, string zipFileName, bool deleteTempFile)
		{
			GZipResult result = new GZipResult();
			
			if (!destFolder.EndsWith("\\"))
			{
				destFolder += "\\";
			}

			if (!srcFolder.EndsWith("\\"))
			{
				srcFolder += "\\";
			}

			string lpTempFile = destFolder + zipFileName + ".tmp";
			string lpZipFile = srcFolder + zipFileName;

			result.TempFile = lpTempFile;
			result.ZipFile = lpZipFile;

			string line = null;
			string lpFilePath = null;
			GZippedFile gzf = null;
			FileStream fsTemp = null;
			ArrayList gzfs = new ArrayList();

			// extract the files from the temp file
			try
			{
				if (!Directory.Exists(destFolder))
					Directory.CreateDirectory(destFolder);

				//files in zip file are all contained in 1 file
				fsTemp = UnzipToTempFile(lpZipFile, lpTempFile, result);
				
				if (fsTemp != null)
				{
					//extract all files from created file
					while (fsTemp.Position != fsTemp.Length)
					{
						line = null;
						while (string.IsNullOrEmpty(line) && fsTemp.Position != fsTemp.Length)
						{
							line = ReadLine(fsTemp);
						}

						if (!string.IsNullOrEmpty(line))
						{

							gzf = GZippedFile.GetGZippedFile(line);
							if (gzf != null && gzf.Length > 0)
							{
								gzfs.Add(gzf);
								lpFilePath = destFolder + gzf.RelativePath;
								gzf.LocalPath = lpFilePath;

								WriteFile(fsTemp, gzf.Length, lpFilePath);
								gzf.Restored = true;
							}
						}
					}
				}
			}
			catch (Exception ex3)
			{
				// handle or display the error 
				throw ex3;
			}
			finally
			{
				if (fsTemp != null)
				{
					fsTemp.Close();
					fsTemp = null;
				}
			}

			// delete the temp file
			try
			{
				if (deleteTempFile)
				{
					File.Delete(lpTempFile);
					result.TempFileDeleted = true;
				}
			}
			catch (Exception ex4)
			{
				// handle or display the error 
				throw ex4;
			}

			result.FileCount = gzfs.Count;
			gzfs.CopyTo(result.Files);
			return result;
		}

		private static string ReadLine(FileStream fs)
		{
			string line = string.Empty;

			const int bufferSize = 4096;
			byte[] buffer = new byte[bufferSize];
			byte b = 0;
			byte lf = 10;
			int i = 0;

			while (b != lf)
			{
				b = (byte)fs.ReadByte();
				buffer[i] = b;
				i++;
			}

			line = System.Text.Encoding.Default.GetString(buffer, 0, i - 1);

			return line;
		}

		private static void WriteFile(FileStream fs, int fileLength, string lpFile)
		{
			FileStream fsFile = null;

			try
			{
				string lpFolder = GetFolder(lpFile);
				
				if (!string.IsNullOrEmpty(lpFolder) && lpFolder != lpFile && !Directory.Exists(lpFolder))
				{
					Directory.CreateDirectory(lpFolder);
				}

				byte[] buffer = new byte[fileLength];
				int count = fs.Read(buffer, 0, fileLength);
				
				int i = 1;
				string tmpFilename = lpFile;
				while (File.Exists(tmpFilename))
				{
					tmpFilename = lpFile;
					tmpFilename += ("(" + i + ")");
					i++;
				}
				
				fsFile = new FileStream(lpFile, FileMode.Create, FileAccess.Write, FileShare.None);
				
				fsFile.Write(buffer, 0, buffer.Length);
				//fsFile.Write(buffer, 0, count);
			}
			catch (Exception ex2)
			{
				// handle or display the error 
				throw ex2;
			}
			finally
			{
				if (fsFile != null)
				{
					fsFile.Flush();
					fsFile.Close();
					fsFile = null;
				}
			}
		}

		private static string GetFolder(string filename)
		{
			string vpFolder = filename;
			int index = filename.LastIndexOf("/");
			
			if (index <= 0)
				index = filename.LastIndexOf("\\");
			
			if (index != -1)
			{
				vpFolder = filename.Substring(0, index + 1);
			}
			
			return vpFolder;
		}

		private static FileStream UnzipToTempFile(string lpZipFile, string lpTempFile, GZipResult result)
		{
			FileStream fsIn = null;
			GZipStream gzip = null;
			FileStream fsOut = null;
			FileStream fsTemp = null;

			const int bufferSize = 4096;
			byte[] buffer = new byte[bufferSize];
			int count = 0;

			try
			{
				fsIn = new FileStream(lpZipFile, FileMode.Open, FileAccess.Read, FileShare.Read);
				result.ZipFileSize = fsIn.Length;

				fsOut = new FileStream(lpTempFile, FileMode.Create, FileAccess.Write, FileShare.None);
				gzip = new GZipStream(fsIn, CompressionMode.Decompress, true);
				while (true)
				{
					count = gzip.Read(buffer, 0, bufferSize);
					if (count != 0)
					{
						fsOut.Write(buffer, 0, count);
					}
					if (count != bufferSize)
					{
						break;
					}
				}
			}
			catch (Exception ex1)
			{
				// handle or display the error 
				throw ex1;
			}
			finally
			{
				if (gzip != null)
				{
					gzip.Close();
					gzip = null;
				}
				if (fsOut != null)
				{
					fsOut.Close();
					fsOut = null;
				}
				if (fsIn != null)
				{
					fsIn.Close();
					fsIn = null;
				}
			}
			
			fsTemp = new FileStream(lpTempFile, FileMode.Open, FileAccess.Read, FileShare.None);
			if (fsTemp != null)
			{
				result.TempFileSize = fsTemp.Length;
			}
			
			return fsTemp;
		}

		private static int GetCompressionPercent(long tempLen, long zipLen)
		{
			double tmp = (double)tempLen;
			double zip = (double)zipLen;
			double hundred = 100;

			double ratio = (tmp - zip) / tmp;
			double pcnt = ratio * hundred;

			return (int)pcnt;
		}
	}
}
